using LightCAD.MathLib;
using System;
using System.Collections;
using System.Collections.Generic;


namespace LightCAD.Three
{
    public class RingGeometry : BufferGeometry
    {
        public class Parameters
        {
            public double innerRadius;
            public double outerRadius;
            public int thetaSegments;
            public int phiSegments;
            public double thetaStart;
            public double thetaLength;
        }

        #region Properties

        public Parameters parameters;

        #endregion

        #region constructor
        public RingGeometry(double innerRadius = 0.5, double outerRadius = 1, int thetaSegments = 32, int phiSegments = 1, double thetaStart = 0, double thetaLength = Math.PI * 2)
        {
            this.type = "RingGeometry";
            this.parameters = new Parameters
            {
                innerRadius = innerRadius,
                outerRadius = outerRadius,
                thetaSegments = thetaSegments,
                phiSegments = phiSegments,
                thetaStart = thetaStart,
                thetaLength = thetaLength
            };
            thetaSegments = Math.Max(3, thetaSegments);
            phiSegments = Math.Max(1, phiSegments);
            // buffers
            var indices = new ListEx<int>();
            var vertices = new ListEx<double>();
            var normals = new ListEx<double>();
            var uvs = new ListEx<double>();
            // some helper variables
            var radius = innerRadius;
            var radiusStep = ((outerRadius - innerRadius) / phiSegments);
            var vertex = new Vector3();
            var uv = new Vector2();
            // generate vertices, normals and uvs
            for (int j = 0; j <= phiSegments; j++)
            {
                for (int i = 0; i <= thetaSegments; i++)
                {
                    // values are generate from the inside of the ring to the outside
                    var segment = thetaStart + i / (double)thetaSegments * thetaLength;
                    // vertex
                    vertex.X = radius * Math.Cos(segment);
                    vertex.Y = radius * Math.Sin(segment);
                    vertices.Push(vertex.X, vertex.Y, vertex.Z);
                    // normal
                    normals.Push(0, 0, 1);
                    // uv
                    uv.X = (vertex.X / outerRadius + 1) / 2.0;
                    uv.Y = (vertex.Y / outerRadius + 1) / 2.0;
                    uvs.Push(uv.X, uv.Y);
                }
                // increase the radius for next row of vertices
                radius += radiusStep;
            }
            // indices
            for (int j = 0; j < phiSegments; j++)
            {
                var thetaSegmentLevel = j * (thetaSegments + 1);
                for (int i = 0; i < thetaSegments; i++)
                {
                    var segment = i + thetaSegmentLevel;
                    var a = segment;
                    var b = segment + thetaSegments + 1;
                    var c = segment + thetaSegments + 2;
                    var d = segment + 1;
                    // faces
                    indices.Push(a, b, d);
                    indices.Push(b, c, d);
                }
            }
            // build geometry
            this.setIndex(indices.ToArray());
            this.setAttribute("position", new Float32BufferAttribute(vertices.ToArray(), 3));
            this.setAttribute("normal", new Float32BufferAttribute(normals.ToArray(), 3));
            this.setAttribute("uv", new Float32BufferAttribute(uvs.ToArray(), 2));
        }
        #endregion

        #region methods
        public RingGeometry copy(RingGeometry source)
        {
            base.copy(source);
            this.parameters = new Parameters()
            {
                innerRadius = source.parameters.innerRadius,
                outerRadius = source.parameters.outerRadius,
                thetaSegments = source.parameters.thetaSegments,
                phiSegments = source.parameters.phiSegments,
                thetaStart = source.parameters.thetaStart,
                thetaLength = source.parameters.thetaLength,
            };
            return this;
        }
        #endregion
    }
}
